﻿namespace ScrumLite.Services.Tests
{
    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using ScrumLite.Services.Contracts;
    using Rhino.Mocks;
    using System.Collections.Generic;
    using System.Globalization;

    [TestClass]
    public class LinqCommitmentsServiceFixture
    {
        MockRepository mocks;
        [TestInitialize]
        public void Initialize()
        {
            this.mocks = new MockRepository();
        }

        [TestMethod()]
        public void ShouldAddCommitmentAndSetInitialEstimateEqualToLeft()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            ScrumLite.Data.Commitment actual = null;

            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(() => store.Add(null))
                .Callback(new Func<ScrumLite.Data.Commitment, bool>(c => { actual = c; return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            Guid projectId = Guid.NewGuid();
            Commitment expected = new Commitment() { Spent = 1, Left = 2, Title = "Title", CreatedBy = "John", CreatedOn = new DateTime(2008, 11, 23), Verification = "Verification" };
            service.Add(projectId.ToString(), expected);

            Assert.AreNotEqual(Guid.Empty, actual.id);
            Assert.AreEqual(expected.CreatedOn, actual.createdOn);
            Assert.AreEqual(expected.CreatedBy, actual.user_createdBy);
            Assert.AreEqual(expected.Left, actual.remaining);
            Assert.AreEqual(expected.Spent, actual.spent);
            Assert.AreEqual(expected.Title, actual.title);
            Assert.AreEqual(expected.Verification, actual.verification);
            Assert.AreEqual(projectId, actual.fk_project);
            Assert.AreEqual(2, actual.initialEstimate);
        }

        [TestMethod()]
        public void ShouldUpdateCommitmentAndNotSetInitialEstimate()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            ScrumLite.Data.Commitment actual = null;

            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(() => store.Update(null))
                .Callback(new Func<ScrumLite.Data.Commitment, bool>(c => { actual = c; return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            Commitment expected = new Commitment() { Id = Guid.NewGuid(), Spent = 1, Left = 2, Title = "Title", CreatedBy = "John", CreatedOn = new DateTime(2008, 11, 23), Verification = "Verification" };
            service.Update(expected);

            Assert.AreEqual(expected.Id, actual.id);
            Assert.AreEqual(expected.CreatedOn, actual.createdOn);
            Assert.AreEqual(expected.CreatedBy, actual.user_createdBy);
            Assert.AreEqual(expected.Left, actual.remaining);
            Assert.AreEqual(expected.Spent, actual.spent);
            Assert.AreEqual(expected.Title, actual.title);
            Assert.AreEqual(expected.Verification, actual.verification);
            Assert.AreEqual(Guid.Empty, actual.fk_project);
            Assert.AreEqual(null, actual.initialEstimate);
        }

        [TestMethod]
        public void ShouldGetStandupDates()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            DateTime[] actual = null;
            DateTime currentDateTime = DateTime.Now;
            DateTime[] expected = new DateTime[] { currentDateTime };

            Guid projectUri = Guid.NewGuid();
            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(store.RetrieveStandupDates(projectUri))
                .Return(expected)
                .Callback(new Func<Guid, bool>(c => { return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            actual = service.GetStandupDates(projectUri.ToString());


            Assert.AreEqual(expected.Length, actual.Length);
            for (int i = 0; i < expected.Length; i++)
            {
                Assert.AreEqual(expected[i], actual[i]);
            }
        }

        [TestMethod]
        public void ShouldGetWeeklyCommitmentsCount()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            Dictionary<int, int> actual = null;
            DateTime currentDateTime = DateTime.Now;
            Dictionary<DateTime, int> expected = new Dictionary<DateTime, int>();
            expected.Add(currentDateTime, 12);
            bool closed = true;

            Guid projectUri = Guid.NewGuid();
            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(store.RetrieveDailyCommitmentsCount(projectUri, closed))
                .Return(expected)
                .Callback(new Func<Guid, bool, bool>((p, c) => { return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            actual = service.GetWeeklyCommitmentsCount(projectUri.ToString(), closed);

            Assert.IsNotNull(actual);
            Assert.AreEqual(1, actual.Count);
            Assert.AreEqual(12, actual[GetYearWeek(currentDateTime)]);
        }

        [TestMethod]
        public void ShouldGetCommitments()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            Commitment[] actual = null;
            ScrumLite.Data.Commitment[] expected = new ScrumLite.Data.Commitment[] { new ScrumLite.Data.Commitment { user_createdBy = "southy", createdOn = DateTime.Today, title = "Title", verification = "Screenshot", initialEstimate = 2, remaining = 1, spent = 1 }};

            Guid projectUri = Guid.NewGuid();
            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(store.RetrieveCommitments(DateTime.Today, projectUri))
                .Return(expected)
                .Callback(new Func<DateTime,Guid,bool>((a,b) => { return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            actual = service.GetCommitments(DateTime.Today, projectUri.ToString());


            Assert.AreEqual(expected.Length, actual.Length);

            for (int i = 0; i < expected.Length; i++)
            {
                Assert.AreEqual(expected[i].user_createdBy, actual[i].CreatedBy);
                Assert.AreEqual(expected[i].createdOn, actual[i].CreatedOn);
                Assert.AreEqual(expected[i].verification, actual[i].Verification);
                Assert.AreEqual(expected[i].title, actual[i].Title);
            }
        }

        [TestMethod]
        public void ShouldGetCommitment()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            Commitment actual = null;
            ScrumLite.Data.Commitment expected = new ScrumLite.Data.Commitment { user_createdBy = "southy", createdOn = DateTime.Today, title = "Title", verification = "Screenshot", initialEstimate = 2, remaining = 1, spent = 1 };

            Guid projectUri = Guid.NewGuid();
            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(store.RetrieveCommitment(projectUri))
                .Return(expected)
                .Callback(new Func<Guid, bool>(c => { return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            actual = service.GetCommitment(projectUri);


            Assert.IsNotNull(actual);
            Assert.AreEqual(expected.user_createdBy, actual.CreatedBy);
            Assert.AreEqual(expected.createdOn, actual.CreatedOn);
            Assert.AreEqual(expected.verification, actual.Verification);
            Assert.AreEqual(expected.title, actual.Title);
        }

        /// <summary>
        /// Gets Weekly Number
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private static int GetYearWeek(DateTime date)
        {
            CultureInfo invariant = CultureInfo.InvariantCulture;
            return invariant.Calendar.GetWeekOfYear(date,
                                   invariant.DateTimeFormat.CalendarWeekRule,
                                   invariant.DateTimeFormat.FirstDayOfWeek);
        }

        [TestMethod()]
        public void ShouldDeleteCommitment()
        {
            ScrumLite.Data.ICommitmentStore store = this.mocks.DynamicMock<ScrumLite.Data.ICommitmentStore>();
            Guid actual = Guid.Empty;

            // set an expectation that will do a callback and set the argument 
            // to a local member to perform Asserts later
            Expect.Call(() => store.Delete(Guid.Empty))
                .Callback(new Func<Guid, bool>(c => { actual = c; return true; }));
            mocks.ReplayAll();

            LinqCommitmentsService service = new LinqCommitmentsService(store);

            Guid expected = Guid.NewGuid();
            service.Delete(expected);

            Assert.AreEqual(expected, actual);
        }
    }
}
